(module interp (lib "eopl.ss" "eopl")
  
  (require "drscheme-init.scm")

  (require "lang.scm")
  (require "data-structures.scm")
  (require "environments.scm")
  (require "store.scm")

  (provide value-of-program value-of/k)

;;;;;;;;;;;;;;;; the interpreter ;;;;;;;;;;;;;;;;

  ;; value-of-program : program -> expval

  (define value-of-program 
    (lambda (pgm)
      (cases program pgm
        (a-program (body)
          (value-of/k body (init-env) (end-cont 1))))))

  ;;;Get Current Continuation depth ;;;;;;;;
  (define get-frames
    (lambda (cont)
      (cases continuation cont
        (end-cont (frames) frames)
        (diff1-cont (exp2 env cont frames)
                    frames)
        (diff2-cont (val1 cont frames)
                    frames)
        (mult1-cont (exp2 env cont frames)
                    frames)
        (mult2-cont (val1 cont frames)
                    frames)
        (rator-cont (rand env cont frames)
                    frames)
        (rand-cont (val1 cont frames)
                   frames)
        (zero1-cont (cont frames)
                    frames)
        (let-exp-cont (var body env cont frames)
                      frames)
        (if-test-cont (exp1 exp2 env cont frames)
                      frames)
        (ref-cont (cont frames)
                  frames)
        
        (begin1-cont(es env cont frames)
                    frames)
;        (begin2-cont (val cont frames)
;                     frames)
        (deref-cont ( cont frames)
                    frames)
        
        (setref1-cont (exp2 env cont frames)
                      frames)
        
        (setref2-cont (val1 cont frames)
                      frames)        
        
        )))
  
  ;; value-of : expression * environment * continuation -> final-expval

  (define value-of/k
    (lambda (exp env cont)
      (begin
        (eopl:printf "Entering value-of/k - \t\t")
        (pretty-print (get-frames cont))
        )
      
      (cases expression exp

        (const-exp (num) (apply-cont cont (num-val num)))

        (var-exp (id) (apply-cont cont (apply-env env id)))

        (diff-exp (exp1 exp2)
          (value-of/k exp1 env
            (diff1-cont exp2 env cont (+ (get-frames cont) 1))))
        
        (mult-exp (exp1 exp2)
                  (value-of/k exp1 env
                              (mult1-cont exp2 env cont (+ (get-frames cont) 1))))

        (proc-exp (bvar body)
          (apply-cont cont
            (proc-val
              (procedure bvar body env))))

        (call-exp (rator rand)
          (value-of/k rator env
            (rator-cont rand env cont (+ (get-frames cont) 1))))
        
        (zero?-exp (exp1)
                   (value-of/k exp1 env
                               (zero1-cont cont (+ (get-frames cont) 1))))
        
        (if-exp (exp0 exp1 exp2)
                (value-of/k exp0 env
                             (if-test-cont exp1 exp2 env cont (+ (get-frames cont) 1))))
        
        (let-exp(var exp1 body)
                (value-of/k exp1 env
                            (let-exp-cont var body env cont (+ (get-frames cont) 1))))

        (begin-exp (e1 es)
                   (value-of/k e1 env
                               (begin1-cont es env cont (+ (get-frames cont) 1))))
        (newref-exp (e1)
                    (value-of/k e1 env
                       (ref-cont cont (+ (get-frames cont) 1))))

        (deref-exp (e1)
          (value-of/k e1 env
                      (deref-cont cont (+ (get-frames cont) 1))))
        
        (setref-exp (e1 e2)
                    (value-of/k e1 env
                                 (setref1-cont e2 env cont (+ (get-frames cont) 1))))      
        ))) 

  ;; apply-cont : continuation * expval -> final-expval

  (define apply-cont
    (lambda (cont val)
      (eopl:printf "Entering Apply Continuaiton - \t")
      ;;(pretty-print cont)
      (pretty-print (get-frames cont))
      (cases continuation cont
        (end-cont (frames) 
      (eopl:printf "Inside End-Cont\n")                  
                  (register-value-with-test-harness val))
        (diff1-cont (exp2 env cont frames)
          ;;(eopl:printf "Start working on first Operand\n")          
          (value-of/k exp2 env (diff2-cont val cont (+ (get-frames cont) 1) )))
        (diff2-cont (val1 cont frames)
         ;;(eopl:printf "Start working on second Operand\n")           
          (let ((n1 (expval->num val1))
                (n2 (expval->num val)))
            (apply-cont cont
              (num-val (- n1 n2)))))
        (mult1-cont (exp2 env cont frames)
                    (value-of/k exp2 env (mult2-cont val cont (+ (get-frames cont) 1))))
        (mult2-cont (val1 cont frames)
                    (let ((n1 (expval->num val1))
                          (n2 (expval->num val)))
                      (apply-cont cont
                                  (num-val (* n1 n2)))))
        (begin1-cont (es env cont frames)
                     (if (null? es)
                         (apply-cont cont val)
                         (value-of/k (car es) env 
                                     (begin1-cont (cdr es) env cont (+ (get-frames cont) 1)))))
        (rator-cont (rand env cont frames)
          (value-of/k rand env
            (rand-cont val cont (+ (get-frames cont) 1))))
        (rand-cont (val1 cont frames)
          (let ((proc (expval->proc val1)))
            (apply-procedure proc val cont)))
        (zero1-cont (cont frames)
                    (apply-cont cont
                                (bool-val
                                 (zero? (expval->num val)))))
        (let-exp-cont (var body env cont frames)
                      (value-of/k body (extend-env var val env) cont))
        (if-test-cont (exp1 exp2 env cont frames)
                      (if (expval->bool val)
                          (value-of/k exp1 env cont)
                          (value-of/k exp2 env cont)))
        (ref-cont (cont frames)
                  (apply-cont cont  (ref-val (newref val))))
                              
        (deref-cont ( cont frames)
           (apply-cont cont
             (deref (expval->ref val))))

        (setref1-cont (exp2 env cont frames)
          (value-of/k exp2 env (setref2-cont val cont (+ (get-frames cont) 1))))
        
        (setref2-cont (val1 cont frames)
          (let ((ref (expval->ref val1))
                (value (expval->num val)))
            (apply-cont cont
              (num-val (begin
                         (setref! ref val)
                         23
                        )))))        
        
        )))
      

  ;; apply-procedure : procedure * expval -> expval

  (define apply-procedure
    (lambda (proc1 val cont)
      (cases proc proc1
        (procedure (bvar body saved-env)
          (value-of/k body
            (extend-env bvar val saved-env)
            cont)))))


  ;; trace has to be in the module where the procedure is defined.
  ;; (trace value-of/k apply-cont)

  )
